home *** CD-ROM | disk | FTP | other *** search
/ GIFs Galore 1993 September / Walnut Creek GIFs Galore CDROM WIN-MAC (Walnut Creek CDROM)(September 1993).iso / pc / macutils / misc / unshar / unshar.c < prev    next >
C/C++ Source or Header  |  1991-11-18  |  19KB  |  799 lines

  1. /*
  2.     File:        unshar.c
  3.  
  4.     Contains:    unshar for MPW that's good enough for
  5.                 comp.sources.unix archives
  6.  
  7.     Written by:    sw
  8.                 Network Analysis Ltd
  9.                 178 Wainbody Ave South
  10.                 Coventry CV3 6BX
  11.                 UK
  12.     Phone:        +44 203 419996
  13.     E-mail:        sw@network-analysis-ltd.co.uk
  14.     
  15.     Copyright:    Public domain
  16.  
  17.     Change History (most recent first):
  18.  
  19.     <9>            18/11/91    sw        Changes to compile with TC 5.0.
  20.     <8>            18/11/91    sw        Deal with embedded "cd"s
  21.     <7>             19/5/91    sw        Remove tests for "if"s and make less sensitive to
  22.                                     variations in shar file formats. Also stop
  23.                                     looking for #! /bin/sh; any comment line will do.
  24.                                     Merge MPW and Think C vers into 1 src file.
  25.     <6>             16/5/91    sw        Handle shar files in sumex archives.
  26.     <5>              3/5/91    sw        Previous mod didn't get the last char of the
  27.                                     filename.
  28.     <4>              3/5/91    sw        Cope with shar files that do not quote the
  29.                                     filename in the "if test -f" line.
  30.     <3>              3/2/91    sw        Pick up terminating string from "sed" or "cat"
  31.                                     command line.
  32.     <2>              5/7/90      sw        Reconvert to MPW tool
  33.     <1>              ??????    aw        Original by Amanda Walker, Intercon
  34.  
  35.     To Do:
  36. */
  37. #ifdef    MPW
  38. #include <Types.h>
  39. #include <StdDef.h>
  40. #include <Files.h>
  41. #include <CursorCtl.h>
  42. #include <StdLib.h>
  43. #endif    MPW
  44. #include <errno.h>
  45. #include <stdio.h>
  46. #include <String.h>
  47. #ifndef NIL
  48. #define NIL    (0L)
  49. #endif
  50.  
  51. /* Strings used in error messages */
  52. #ifdef    MPW
  53. #define    CPSTR        c2pstr
  54. #define PCSTR        p2cstr
  55. #else
  56. #define CPSTR        CtoPstr
  57. #define PCSTR        PtoCstr
  58. #endif
  59.  
  60. #ifndef EOF
  61. #define EOF    (-1L)
  62. #endif
  63.  
  64. #ifndef    MPW
  65. /*    for Think C
  66. */
  67.  
  68. /* if using new Think C headers, uncomment the following line, otherwise
  69.    use the line after that. Only one of the following pair of lines should be used
  70. */
  71. #define    LOWMEM_LONG(lowMemVar)    (*((long *)lowMemVar))        /* use this if new-style TC headers */
  72. /*
  73. #define LOWMEM_LONG(lowMemVar)    (lowMemVar)                    /* use this if old-style TC headers */
  74.  
  75. #define DIRECTORY(pb)    (((pb).dirInfo.ioFlAttrib & 0x10) == 0x10)
  76. #define    getDir    11            /* buttons in the dialogue box */
  77. #define    getCurDir    12
  78. #define    GD_PROMPT    13
  79.  
  80. typedef enum
  81. {
  82.     infoDlgRes = 1000,
  83.     aboutAlrtRes,            /* About... alert resource number */
  84.     abortRes,
  85.     gdDlgRes,
  86.     typeDlgRes,
  87.     errorAlertRes,
  88.     dupFNAlertRes
  89. };
  90.  
  91.  
  92. typedef enum                /* File menu item numbers */
  93. {
  94.     extract = 1,
  95.     close,
  96.     quit
  97. };
  98.  
  99.  
  100. typedef enum                 /* Edit menu item numbers */
  101. {
  102.     undo = 1,
  103.     /* --- */
  104.     cut = 3,
  105.     copy,
  106.     paste,
  107.     clear
  108. };
  109.  
  110. typedef enum                /* Option menu item numbers */
  111. {
  112.     forceOpt = 1,
  113.     setCr
  114. };
  115.  
  116. typedef enum                /* dialog item numbers */
  117. {
  118.     okB = 1,
  119.     crText,
  120.     cancelB
  121. };
  122.  
  123. typedef enum
  124. {
  125.     continueB = 1,
  126.     quitB,
  127.     messageItem
  128. };
  129.  
  130. MenuHandle    fileM, editM, optM;
  131. short        dirVRefNum = 0;
  132. char        *progname;
  133. short        appFileCount,
  134.             whatToDo;
  135. DialogPtr    infoDlgPtr, typeDlgPtr;
  136.  
  137. #endif                                /* Think C declarations */
  138.  
  139. Boolean force =     false;          /* force overwriting existing files */
  140. OSType    fdCreator =    'MPS ';            /* Finder Creator */
  141. OSType    fdType =    'TEXT';            /* Finder Type */
  142.  
  143. void ErrMsg (char *p1, char *p2);
  144.                                     /* for display errors */
  145. void ConvertFName (char *unixfilename, char *mpwfilename);
  146.                                     /* for converting Unix->Mac filenames */
  147. void unshar(char *s);                /* the guts of the program */
  148.  
  149. #ifndef    MPW
  150. /*    Prototypes for Think C standalone version */
  151. void SetDText(DialogPtr dlog, int item, Str255 str);
  152. void GetDText(DialogPtr dlog, int item, StringPtr str);
  153. void GetCreator(void);
  154. void DoFileMenu(int item);
  155. void DoEditMenu(int item);
  156. void DoOptMenu(int item);
  157. void DoAbout(void);
  158. pascal short DirSelHook(int item, DialogPtr theDialog);
  159. pascal Boolean DirFilterProc(CInfoPBPtr pb);
  160. long GetDir(char *text);
  161. void GetNextFile(SFReply *fInfoPtr);
  162. void Extract(void);
  163.  
  164.  
  165. void SetDText(DialogPtr dlog, int item, Str255 str)
  166. {
  167. Handle    itemHandle;
  168. short    itemType;
  169. Rect    itemRect;
  170.  
  171.     GetDItem (dlog, item, &itemType, &itemHandle, &itemRect);
  172.     SetIText (itemHandle, str);
  173. }
  174.  
  175. /* Dialog handler */
  176.  
  177. void GetDText(DialogPtr dlog, int item, StringPtr str)
  178. {
  179. Handle    itemHandle;
  180. short    itemType;
  181. Rect    itemRect;
  182.  
  183.     GetDItem (dlog, item, &itemType, &itemHandle, &itemRect);
  184.     GetIText (itemHandle, str);
  185. }
  186.  
  187.  
  188. void GetCreator(void)
  189. {
  190.     short    itemHit;
  191.     char    creator[5];
  192.  
  193.     typeDlgPtr = GetNewDialog (typeDlgRes, NIL, (WindowPtr) -1L);
  194.     BlockMove(&fdCreator, creator+1, 4);
  195.     creator[0] = '\004';
  196.     SetDText(typeDlgPtr, crText, (StringPtr)creator);
  197.     SelectWindow(typeDlgPtr);
  198.     ShowWindow(typeDlgPtr);
  199.     DrawDialog(typeDlgPtr);
  200.     ModalDialog(NIL, &itemHit);
  201.     if (itemHit == okB) {
  202.         GetDText (typeDlgPtr, crText,(StringPtr) &creator);
  203.         BlockMove(creator+1, &fdCreator, 4);
  204.     }
  205.     DisposDialog(typeDlgPtr);
  206. }
  207.  
  208.  
  209. /*
  210.     File menu handler
  211. */
  212.  
  213. void DoFileMenu(int item)
  214. {
  215. WindowPeek    wPeek;
  216.  
  217.     switch (item)
  218.     {
  219.         case extract:
  220.             Extract();
  221.             break;
  222.         case close:
  223.             if ((wPeek = (WindowPeek) FrontWindow ()) != NIL)
  224.             {
  225.                 if (wPeek->windowKind < 0)
  226.                     CloseDeskAcc (wPeek->windowKind);
  227.             }
  228.             break;
  229.         case quit:
  230.             SkelWhoa ();
  231.             break;
  232.     }
  233. }
  234.  
  235.  
  236. void DoEditMenu(int item)
  237. {
  238. DialogPtr    theDialog;
  239.  
  240.     theDialog = (DialogPtr) FrontWindow ();
  241.     if (((WindowPeek) theDialog)->windowKind != dialogKind)
  242.         return;
  243.  
  244.     switch (item)
  245.     {
  246.         case cut:
  247.         {
  248.             DlgCut (theDialog);
  249.             (void) ZeroScrap ();
  250.             (void) TEToScrap ();
  251.             break;
  252.         }
  253.  
  254.         case copy:
  255.         {
  256.             DlgCopy (theDialog);
  257.             (void) ZeroScrap ();
  258.             (void) TEToScrap ();
  259.             break;
  260.         }
  261.  
  262.         case paste:
  263.         {
  264.             (void) TEFromScrap ();
  265.             DlgPaste (theDialog);
  266.             break;
  267.         }
  268.  
  269.         case clear:
  270.         {
  271.             DlgDelete (theDialog);
  272.             break;
  273.         }
  274.     }
  275. }
  276.  
  277. void DoOptMenu(item)
  278. int        item;
  279. {
  280.     switch (item)
  281.     {
  282.         case forceOpt:    force = !force;
  283.                                         CheckItem(optM, forceOpt, force);
  284.                                         break;
  285.          case setCr:        GetCreator();
  286.                                      break;
  287.      }
  288. }
  289.  
  290. /*
  291.     Handle selection of About╔ item from Apple menu
  292. */
  293.  
  294. void DoAbout(void)
  295. {
  296.     (void) Alert (aboutAlrtRes, NIL);
  297. }
  298.  
  299. Boolean        useCurDir;        /* Set if current dir to be used */
  300.  
  301. /* Filter procs & dialogue hooks to select directories only in SFGet file */
  302. pascal short DirSelHook(int item, DialogPtr theDialog)
  303. {
  304.     if (item == getDir|| item == getCurDir) {
  305.         /* folder selected */
  306.         useCurDir = item == getCurDir;
  307.         item = getOpen;
  308.     }
  309.     return(item);
  310. }
  311.  
  312. pascal Boolean DirFilterProc(CInfoPBPtr pb)
  313. {
  314.     return(!DIRECTORY(*pb));    /* a directory if bit 4 is set */
  315. }
  316. /*
  317.  * GetDir - manage the directory selection dialog
  318.  */
  319. long GetDir(char *text)
  320. {
  321.     Point        where;
  322.     short        ht, wd;
  323.     SFReply        reply = {0};
  324.     CInfoPBRec    pb;
  325.     DialogPtr    dlgP;
  326.     long        dirDirID=0;        /* Selected directory DirID */
  327.  
  328.     if ((dlgP = GetNewDialog(gdDlgRes, NIL,(WindowPtr) -1)) == NIL) {
  329.         return(TRUE);
  330.     }
  331.     wd = (dlgP->portRect.right)-(dlgP->portRect.left);
  332.     ht = (dlgP->portRect.bottom)-(dlgP->portRect.top);
  333.     /* centre the dialogue box on the screen
  334.        (but how do I know which screen?)
  335.     */
  336.     where.h = (screenBits.bounds.right-screenBits.bounds.left-wd) / 2;
  337.     where.v = (screenBits.bounds.bottom-screenBits.bounds.top-ht) / 2;
  338.     ParamText((StringPtr)text, (StringPtr)"\p", (StringPtr)"\p", (StringPtr)"\p");
  339.     SFPGetFile (where,
  340.                 (StringPtr)text,
  341.                 (FileFilterProcPtr) DirFilterProc,
  342.                 -1, NIL,
  343.                 (DlgHookProcPtr) DirSelHook,
  344.                 &reply,
  345.                 gdDlgRes, NIL);
  346.     if (reply.good) {
  347.         dirVRefNum = reply.vRefNum;
  348.         if (useCurDir) {
  349.             dirDirID = *((long *)CurDirStore);
  350.         }
  351.         else {
  352.             dirDirID = (long)(reply.fType);
  353.         }
  354.     }
  355.     return(dirDirID);
  356. }
  357.  
  358. void GetNextFile(SFReply *fInfoPtr)
  359. {    AppFile    nextFile;
  360.     static    short    idx=1;
  361.  
  362.     GetAppFiles(idx++, &nextFile);
  363.     fInfoPtr->vRefNum = nextFile.vRefNum;
  364.     fInfoPtr->fType = nextFile.fType;
  365.     fInfoPtr->version = nextFile.versNum;
  366.     BlockMove(nextFile.fName, fInfoPtr->fName, (int)(nextFile.fName[0]+1));
  367.     appFileCount--;
  368. }
  369.  
  370. void Extract(void)
  371. {
  372.     Point        where;
  373.     SFReply        reply;
  374.  
  375.     while (1){
  376.         if (appFileCount > 0) GetNextFile(&reply);
  377.         else {
  378.             /*
  379.              * Use the standard file dialog to select the archive.
  380.              */
  381.             where.h = where.v = 75;
  382.             SFGetFile(where, (StringPtr)"\pSelect shar file", NIL, -1, NIL, NIL, &reply);
  383.             if (!reply.good)
  384.                 return;
  385.         }
  386.     
  387.         /*
  388.          * Remember the VRefNum and Name for OpenArchive.
  389.          * Find out where to put the extracted files.
  390.          */
  391.         (void) SetVol(NIL, reply.vRefNum);
  392.         PtoCstr(reply.fName);
  393.         unshar((char *)reply.fName);
  394.         (void) SetVol(NIL, reply.vRefNum);
  395.     }
  396. }
  397.  
  398. main(int argc, char *argv[])
  399. {
  400.  
  401.     Handle        fTypeH;
  402.     DialogTHndl    dlgH;
  403.     Point        where;
  404.     short        ht, wd;
  405.         
  406.     SkelInit (3, NIL);
  407.     SkelApple ("\pAbout Unshar╔", DoAbout);
  408.  
  409.     fileM = NewMenu (1000, (StringPtr)"\pFile");
  410.     AppendMenu (fileM, (StringPtr)"\pExtract/O;Close/K;Quit/Q");
  411.     SkelMenu (fileM, DoFileMenu, NIL, FALSE);
  412.  
  413.     editM = NewMenu (1001, (StringPtr)"\pEdit");
  414.     AppendMenu (editM, (StringPtr)"\p(Undo/Z;(-;Cut/X;Copy/C;Paste/V;Clear");
  415.     SkelMenu (editM, DoEditMenu, NIL, FALSE);
  416.  
  417.     optM = NewMenu (1002, (StringPtr)"\pOptions");
  418.     AppendMenu (optM, (StringPtr)"\pOverwrite existing files;File type...");
  419.     SkelMenu (optM, DoOptMenu, NIL, TRUE);
  420.     
  421.     CountAppFiles (&whatToDo, &appFileCount);
  422.     if ((fTypeH = (char **)GetResource('ftyp', 0)) == NIL) {
  423.         ExitToShell();
  424.     }
  425.     BlockMove(*fTypeH, &fdCreator, 4);
  426.     BlockMove((*fTypeH)+4, &fdType, 4);
  427.     ReleaseResource(fTypeH);
  428.     
  429.     CouldDialog(typeDlgRes);
  430.     CouldDialog(infoDlgRes);
  431.     dlgH = (DialogTHndl)GetResource('DLOG', typeDlgRes);
  432.     wd = ((* dlgH)->boundsRect.right)-((* dlgH)->boundsRect.left);
  433.     /* centre the dialogue box on the screen
  434.        (but how do I know which screen?)
  435.     */
  436.     where.h = (screenBits.bounds.right-screenBits.bounds.left-wd) / 2;
  437.     (* dlgH)->boundsRect.right += where.h;
  438.     (* dlgH)->boundsRect.left = where.h;
  439.     
  440.     dlgH = (DialogTHndl)GetResource('DLOG', infoDlgRes);
  441.     wd = ((* dlgH)->boundsRect.right)-((* dlgH)->boundsRect.left);
  442.     /* centre the dialogue box on the screen
  443.        (but how do I know which screen?)
  444.     */
  445.     where.h = (screenBits.bounds.right-screenBits.bounds.left-wd) / 2;
  446.     (* dlgH)->boundsRect.right += where.h;
  447.     (* dlgH)->boundsRect.left = where.h;
  448.  
  449.     /* if launched with a set of files, extract them first before asking
  450.        the user for more
  451.     */
  452.     if (appFileCount > 0)
  453.         Extract();
  454.     SkelMain ();
  455.     SkelClobber ();
  456.     FreeDialog(typeDlgRes);
  457.     FreeDialog(infoDlgRes);
  458. }
  459. #else                                /* MPW main support routines */
  460.  
  461. main(int argc, char *argv[])
  462. {
  463.     char    **filelist;                /* list of files to process */
  464.     int        fileCount = 0;            /* no of files to process */    
  465.  
  466. #ifdef    MPW
  467.     InitCursorCtl(nil);
  468. #endif    MPW
  469.     argc--; argv++;
  470.     if ((filelist = (char **)calloc((size_t)argc, sizeof(Ptr))) == NULL) {
  471.         fprintf(stderr, "### Not enough memory\n");
  472.         exit(-1);
  473.     }
  474.     while (argc) {
  475.         if (argv[0][0] == '-') {
  476.             switch (argv[0][1]) {
  477.             case '\0':
  478.                 filelist[fileCount++] = "-";
  479.                 break;
  480.             case 'c':
  481.                 /* creator is in next arg */
  482.                 argc--;
  483.                 argv++;
  484.                 strncpy((char *)&fdCreator, argv[0], (size_t)4);
  485.                 break;
  486.             case 'f':
  487.                 force = true;
  488.                 break;
  489.             case 't':
  490.                 /* type is in next arg */
  491.                 argc--;
  492.                 argv++;
  493.                 strncpy((char *)&fdType, argv[0], (size_t)4);
  494.                 break;
  495.             default:
  496.                 fprintf(stderr, "### Usage: unshar -f -c creator -t type [files|-]\n");
  497.                 exit(-1);
  498.             }
  499.         }
  500.         else
  501.             filelist[fileCount++] = argv[0];
  502.         argc--; argv++;
  503.     }
  504.     /* at this point all files to process are in filelist */
  505.     for (argc = 0; argc < fileCount; argc++) {
  506.         unshar(filelist[argc]);
  507.     }
  508. }
  509. #endif
  510.  
  511. /*    This part (mostly) common to both MPW and Think C */
  512.  
  513. void ErrMsg (char *p1, char *p2)
  514. {
  515. #ifdef    MPW
  516.     fprintf (stderr, "### %P %P\n", p1, p2);
  517. #else
  518.     ParamText((StringPtr)p1, (StringPtr)p2, (StringPtr)"\p", (StringPtr)"\p");
  519.     (void)StopAlert(errorAlertRes, NIL);
  520. #endif
  521. }
  522.  
  523. void ConvertFName (char *unixfilename, char *macfilename)
  524. {
  525.     char    *cp, *tp;
  526.     char    buf[256];
  527.     Boolean    slashSeen = false;
  528.     
  529.     /* make a Mac relative pathname */
  530.     /*    rules of the game:
  531.         'foo' -> :foo
  532.         "foo" -> :foo
  533.         `foo` -> :foo
  534.         ./foo -> :foo
  535.         foo/baz -> :foo:baz
  536.         .foo -> :_foo
  537.         .. -> ::
  538.         foo/./baz -> :foo:baz
  539.         /foo/baz -> :foo:baz       because we don't really want to create files at top vol level
  540.         foo:baz -> :foo/baz
  541.         rules apply recursively
  542.     */
  543.     
  544.     (void) strcpy(macfilename, ":");            /* initialize with a leading colon */
  545.     for (cp = buf, tp = unixfilename; *tp; tp++) {
  546.         switch (*tp) {
  547.             case '\'':
  548.             case '"':
  549.             case '`':    break;                    /* delete these chars */
  550.             case '.':    if (*(tp+1) == '/') {    /* delete any occurence of ./ */
  551.                             tp++;
  552.                         }
  553.                         else if (*(tp+1) == '.') {
  554.                             *cp++ = ':';
  555.                             *cp++ = ':';        /* convert .. to :: */
  556.                             tp++;
  557.                         }
  558.                         else if (cp == buf)
  559.                             *cp++ = '_';        /* replace leading dot with _ */
  560.                         else
  561.                             *cp++ = '.';
  562.                         break;                    
  563.             case '/':    if (cp != buf)
  564.                             *cp++ = ':';        /* replace / with : */
  565.                         slashSeen = true;
  566.                         break;
  567.             case ':':    *cp++ = '/';            /* replace : with / */
  568.                         break;
  569.             default:    *cp++ = *tp;
  570.                         break;
  571.         }
  572.     }
  573.     *cp = '\0';
  574.     
  575.     if (slashSeen)
  576.         (void) strcat(macfilename, buf);
  577.     else
  578.         (void) strcpy(macfilename, buf);
  579.  
  580. }
  581.  
  582. void unshar(char *s)
  583. {
  584.   char        buffer[BUFSIZ];
  585.   char        *cp;
  586.   FILE        *infp, *outfp;
  587.   char        unixfilename[256], mpwfilename[256];
  588.   char        *tp, *ts, delim;
  589.   char        terminator[30];
  590.   int        tlen;
  591.   int        line;
  592.   long        dirID, dID;
  593.   short        volRef;
  594.   FInfo        fileInfo;
  595.   OSErr        err;
  596. #ifndef    MPW
  597.   WDPBRec    wdpb;
  598.   char        lineNo[10];
  599. #endif
  600.  
  601. #ifdef    MPW
  602.   if (strcmp(s, "-") == 0) {
  603.       infp = stdin;
  604.     ErrMsg("\pProcessing std input:","");
  605.   }
  606.   else {
  607.       infp = fopen(s, "r");
  608.       CPSTR(s);
  609.       if (!infp) {
  610.         ErrMsg("\pCould not open file", s);
  611.         exit(-1);
  612.       }
  613.       else ErrMsg("\pProcessing", s);
  614.   }
  615. #else    /* Think C */
  616.   infp = fopen(s, "r");
  617.   CPSTR(s);
  618.   if (!infp) {
  619.     ErrMsg((char*)"\pCould not open file", s);
  620.     return;
  621.   }
  622. #endif
  623.  
  624.   /* skip over news header lines etc. */
  625.   for (line = 1; cp = fgets(buffer, sizeof(buffer), infp); line++)
  626.     if (buffer[0] == '#' || buffer[0] == ':') break;
  627.     
  628.   if (!cp) {
  629.     ErrMsg((char*)"\pCould not locate start of archive in file", s);
  630.     exit(-1);
  631.   }
  632.  
  633. #ifndef    MPW
  634.   /* Think C version needs to ask user to locate target folder */
  635.  
  636.   /*
  637.    * Open the target directory as a base to put everything.
  638.    */
  639.   if ((dirID = GetDir((char*)"\pin which to put extracted files")) == 0L)
  640.       return;
  641.   
  642.   dID = dirID;            /* dID may be changed if subfolders are created */
  643.   (void)HSetVol(NIL, dirVRefNum, dirID);
  644.   
  645.   /* now we should be at the start of the shar archive itself */
  646.   infoDlgPtr = NIL;
  647.   infoDlgPtr = GetNewDialog (infoDlgRes, NIL, (WindowPtr)-1L);
  648. #endif
  649.  
  650.   HGetVol((StringPtr)buffer, &volRef, &dID);        /* get current dir, vol */
  651.   while (cp = fgets(buffer, sizeof(buffer), infp)) {
  652.     line++;
  653. #ifdef    MPW
  654.     SpinCursor(-1);
  655. #endif    MPW
  656.  
  657.     if (buffer[0] == '#' || buffer[0] == ':') continue;    /* comment line */
  658.     if (strncmp(buffer, "exit", 4) == 0) { break; }  /* exit */
  659.  
  660.     /*    there are 2 types of shar files:
  661.         a) the ones that use "cat"
  662.         b) the ones that use "sed"
  663.     */
  664.     if (strncmp(buffer, "sed", 3) == 0 ||
  665.         strncmp(buffer, "cat", 3) == 0) {
  666.           sscanf(buffer, "%*[^>]>%s", unixfilename);
  667.           /* make Mac relative pathname */
  668.           ConvertFName(unixfilename, mpwfilename);
  669.  
  670.           /* work out the terminating string */
  671.           sscanf(buffer, "%*[^<]<<%s", terminator);
  672.           tp = terminator;
  673.           tlen = strlen(terminator);
  674.           
  675.           while (*tp == ' ') tp++;        /* skip whitespace */
  676.           switch (*tp) {
  677.               case '\\':    ts = tp + 1;    /* start of term string */
  678.                             delim = ' ';
  679.                             break;
  680.               case '"':        ts = tp + 1;
  681.                             delim = *tp;
  682.                             break;
  683.               case '\'':    ts = tp + 1;
  684.                             delim = *tp;
  685.                             break;
  686.               default:        ts = tp;
  687.                             delim = ' ';
  688.                             break;                
  689.           }
  690.           do {
  691.               tp++;
  692.           } while (*tp != '\0' && *tp != delim);
  693.           *tp = '\0';
  694.           tlen = tp - ts;
  695.  
  696.           outfp = fopen(mpwfilename, "r");
  697.           CPSTR(mpwfilename);
  698.           if (outfp && !force) {
  699.             fclose(outfp);
  700. #ifdef    MPW
  701.             ErrMsg("\pWill not overwrite existing file", mpwfilename);
  702. #else
  703.             ParamText((StringPtr)"\pWill not overwrite existing file",
  704.                       (StringPtr)mpwfilename, (StringPtr)"\p", (StringPtr)"\p");
  705.             if (NoteAlert(dupFNAlertRes, NIL) == quitB) break;
  706. #endif
  707.             while (strncmp(buffer, ts, tlen) != 0) {  /* skip to terminating string */
  708.               fgets(buffer, sizeof(buffer), infp);
  709.               line++;
  710.             }
  711.           } else {
  712.             if (outfp) fclose(outfp);
  713.             if (HCreate(volRef, dID, (StringPtr)mpwfilename, fdCreator, fdType) != noErr) {
  714.                 ErrMsg((char*)"\pCouldn't create", mpwfilename);
  715.                 exit(-1);
  716.             }
  717. #ifdef MPW
  718.             fprintf(stderr, "  Open \"%P\"\n", mpwfilename);
  719. #else
  720.             ParamText((StringPtr)mpwfilename,
  721.                       (StringPtr)"\p", (StringPtr)"\p", (StringPtr)"\p");
  722.             SelectWindow(infoDlgPtr);
  723.             ShowWindow(infoDlgPtr);
  724.             DrawDialog(infoDlgPtr);         
  725. #endif
  726.             PCSTR((StringPtr)mpwfilename);
  727.             if ((outfp = fopen(mpwfilename, "w")) == NULL) {
  728.                 CPSTR(mpwfilename);
  729.                 ErrMsg((char*)"\pCouldn't open", mpwfilename);
  730.                 exit(-1);
  731.             }
  732.             
  733.             if (strncmp(buffer, "sed", 3) == 0) {
  734.                 fgets(buffer, sizeof(buffer), infp);
  735.                 do {
  736.                   fputs(buffer+1, outfp);
  737.                   fgets(buffer, sizeof(buffer), infp);
  738.                   line++;
  739. #ifdef    MPW
  740.                   SpinCursor(1);
  741. #endif    MPW
  742.                 } while (strncmp(buffer, ts, tlen) != 0);
  743.             }
  744.             else {
  745.                 /* copy everything up to terminating string to output file */
  746.                 fgets(buffer, sizeof(buffer), infp);
  747.                 while (strncmp(buffer, ts, tlen) != 0) {
  748.                     fputs(buffer, outfp);
  749.                     fgets(buffer, sizeof(buffer), infp);
  750.                     line++;
  751. #ifdef    MPW
  752.                     SpinCursor(1);
  753. #endif    MPW
  754.                 }
  755.             }
  756.             fclose(outfp);
  757. #ifndef    MPW
  758.             HideWindow((WindowPtr)infoDlgPtr);
  759. #endif
  760.             /* set file type and creator */
  761.             CPSTR(mpwfilename);
  762.             GetFInfo((StringPtr)mpwfilename,0,&fileInfo);
  763.             fileInfo.fdType = fdType;
  764.             fileInfo.fdCreator = fdCreator;
  765.             SetFInfo((StringPtr)mpwfilename, 0, &fileInfo);
  766.             PCSTR((StringPtr)mpwfilename);
  767.           }
  768.       } else if (strncmp(buffer, "if test ! -d", 12) == 0 ||
  769.                    strncmp(buffer, "if `test ! -d", 13) == 0) {
  770.         /* testing to see if a directory is there */
  771.         if (sscanf(buffer, "if test ! -d '%s'", unixfilename) == 1 ||
  772.             sscanf(buffer, "if test ! -d %s", unixfilename) == 1 ||
  773.             sscanf(buffer, "if `test ! -d %s`", unixfilename) == 1) {
  774.           /* make Mac relative pathname */
  775.           ConvertFName(unixfilename, mpwfilename);
  776.             
  777.           /* I wish MPW C had mkdir(), but at least we don't have to do parameter blocks */
  778.           CPSTR(unixfilename);
  779.           CPSTR(mpwfilename);
  780.           HGetVol((StringPtr)unixfilename, &volRef, &dirID);
  781.           err = DirCreate(volRef, dirID, (StringPtr)mpwfilename, &dID);
  782.         }
  783.     } else if (strncmp(buffer, "cd ", 3) == 0) {
  784.         if (sscanf(buffer, "cd '%s'", unixfilename) == 1 ||
  785.             sscanf(buffer, "cd %s", unixfilename) == 1)     {
  786.             /* change the default directory */
  787.             ConvertFName(unixfilename, mpwfilename);
  788.              CPSTR(mpwfilename);
  789. #ifdef MPW
  790.             fprintf(stderr, "###  Change directory to \"%P\"\n", mpwfilename);
  791. #endif
  792.             HSetVol((StringPtr)mpwfilename, 0, 0L);
  793.             HGetVol((StringPtr)unixfilename, &volRef, &dID);
  794.         }
  795.     }
  796.   }
  797.   fclose(infp);
  798. }
  799.